/*
 *  Renderer.cpp
 *  
 *
 *  Created by Daniel Condon on 20/07/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */

#include <GLUT/glut.h>
#include <OpenGL/glu.h>
#include <OpenGL/gl.h>
#include <OpenGL/OpenGL.h>

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <math.h>

#include "IBMSystem/IBMController.h"
#include "CellSystem/CellularAutomation.h"

const int VIEW_DISTANCE_IBM = 100;
const int VIEW_DISTANCE_CELL = 500;

const int MatrixColumns = 50;
const int MatrixRows = 50;

const int creatureSize = 5.0;

double mouseXObjectSpace, mouseYObjectSpace;

using namespace std;

class Renderer{
private:
	bool CellMode;
	bool DEBUG;
	
	int VIEW_DISTANCE;
	
	double divisionWidth;
	double divisionHeight;
	
	CellularAutomation* cellMatrix;
	IBMController* IBMMonitor;
	
	void DrawDetectCircle(double X, double Y, int size){
		glBegin(GL_LINE_LOOP);
		for (unsigned int angle = 0; angle < 360; angle+=5) {
			float deg = angle*3.1415/180;
			glVertex2f(X + sin(deg) * size, Y + cos(deg) * size);
		}
		glEnd();
	}
public:
	void Init(){
		glClearColor(0.0, 0.0, 0.0, 0.0);
		glClear(GL_COLOR_BUFFER_BIT);
		glEnable(GL_CULL_FACE);
		glEnable (GL_BLEND);
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		CellMode = true;
		VIEW_DISTANCE = VIEW_DISTANCE_CELL;
		DEBUG = false;
	}
	
	void InitCellMatrix(CellularAutomation* c){
		cellMatrix = c;
	}
	
	void InitIBM(IBMController* i){
		IBMMonitor = i;
	}
	
	void Click(int x, int y){
		mouseXObjectSpace = x;
		mouseYObjectSpace = y;
	}
	
	void DrawGround(){
		
	}
	
	void ToggleDebug(bool set){
		DEBUG = set;
	}
	
	void SwapMode(int which){
		if (which == 0) {
			CellMode = true;
			VIEW_DISTANCE = VIEW_DISTANCE_CELL;
		}else {
			CellMode = false;
			VIEW_DISTANCE = VIEW_DISTANCE_IBM;
		}

	}
	
	void Draw2DCreatures(){
		for (int i = 0; i < IBMMonitor->getNumCreatures(); i++) {
			Draw2DCreature(IBMMonitor->getCreature(i));
		}
	}
	
	void Draw2DCreature(Brain* cb){
		if (DEBUG) {
			vector<Brain*> joins = cb->connected();
			glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
			if (cb->getAlive()) {
				for (int b = 0; b < joins.size(); b++) {
					glBegin(GL_LINES);
					glVertex2f(cb->getPos().getX(), cb->getPos().getY());
					glVertex2f(joins.at(b)->getPos().getX(), joins.at(b)->getPos().getY());
					glEnd();
				}
			}
			
			
			//draw detect circles
			glColor4f(0.0f, 1.0f, 0.0f, 0.5f);
			DrawDetectCircle(cb->getPos().getX(), cb->getPos().getY(), 40.0);
		}
		
		switch (cb->infectionStatus()) {
			case 0:
				glColor4f(0.0f,0.0f,1.0f,1.0f);
				break;
			case 1:
				glColor4f(0.0f, 0.8f, 0.2f, 1.0f);
				break;
			case 2:
				glColor4f(0.6f, 0.4f, 0.0f, 1.0f);
				break;
			case 3:
				glColor4f(0.75f, 0.75f, 0.75f, 0.3f);
				break;
			default:
				glColor4f(1.0f, 1.0f ,1.0f ,1.0f);
				break;
		}
		glBegin(GL_LINE_LOOP);
		glVertex2f(cb->getPos().getX()-creatureSize, cb->getPos().getY()+creatureSize);
		glVertex2f(cb->getPos().getX()+creatureSize, cb->getPos().getY()+creatureSize);
		glVertex2f(cb->getPos().getX()+creatureSize, cb->getPos().getY()-creatureSize);
		glVertex2f(cb->getPos().getX()-creatureSize, cb->getPos().getY()-creatureSize);
		glEnd();
	}
	
	void DrawCellBorders(){
		glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
		divisionWidth = VIEW_DISTANCE/MatrixColumns;
		divisionHeight = VIEW_DISTANCE/MatrixRows;
		glBegin(GL_LINES);
		
		for (int x = 0; x < MatrixColumns; x++) {
			glVertex2f(x*divisionWidth, 0);
			glVertex2f(x*divisionWidth, VIEW_DISTANCE);
		}
		for (int y = 0; y < MatrixRows; y++) {
			glVertex2f(0, y*divisionHeight);
			glVertex2f(VIEW_DISTANCE, y*divisionHeight);
		}
		glEnd();
	}
	
	void DrawBarriers(int w, int h){
		for (int i = 0; i < 4; i++) {
			if (cellMatrix->getCell(w,h)->hasBarrier(i)) {
				glColor4f(1.0,0.0,0.0,1.0);
				glBegin(GL_LINES);
				switch (i) {
					case 0:
						glVertex2f((w+1)*divisionWidth, h*divisionHeight);
						glVertex2f((w+1)*divisionWidth, (h+1)*divisionHeight);
						break;
					case 1:
						glVertex2f((w+1)*divisionWidth, (h+1)*divisionHeight);
						glVertex2f(w*divisionWidth, (h+1)*divisionHeight);
						break;
					case 2:
						glVertex2f(w*divisionWidth, (h+1)*divisionHeight);
						glVertex2f(w*divisionWidth, h*divisionHeight);
						break;
					case 3:
						glVertex2f(w*divisionWidth, h*divisionHeight);
						glVertex2f((w+1)*divisionWidth, h*divisionHeight);
						break;
					default:
						break;
				}
				glEnd();
			}
		}
		
	}
	
	void DrawCells(){
		divisionWidth = VIEW_DISTANCE/MatrixColumns;
		divisionHeight = VIEW_DISTANCE/MatrixRows;
		for (int w = 0; w < MatrixColumns; w++) {
			for (int h = 0; h < MatrixRows; h++) {
				if (cellMatrix->getCell(w,h)->getCellCurrent() > 0) {
					double fill = cellMatrix->getCell(w,h)->getCellCurrent()/cellMatrix->getCell(w,h)->getCellMax();
					if (fill > 1.0f) {
						glColor4f(1.0, 0.0, 0.0, fill);
					}else {
						glColor4f(1.0f, 1.0f, 1.0f, fill);
					}				
					glBegin(GL_QUADS);
					glVertex2f(w*divisionWidth, h*divisionHeight);
					glVertex2f((w+1)*divisionWidth, h*divisionHeight);
					glVertex2f((w+1)*divisionWidth, (h+1)*divisionHeight);
					glVertex2f(w*divisionWidth, (h+1)*divisionHeight);
					glEnd();
				}
				DrawBarriers(w, h);
			}
		}
	}
	
	void DrawSelectedCell(){
		divisionWidth = VIEW_DISTANCE/MatrixColumns;
		divisionHeight = VIEW_DISTANCE/MatrixRows;
		
		glBegin(GL_QUADS);
		glVertex2f(mouseXObjectSpace*divisionWidth, mouseYObjectSpace*divisionHeight);
		glVertex2f((mouseXObjectSpace+1)*divisionWidth, mouseYObjectSpace*divisionHeight);
		glVertex2f((mouseXObjectSpace+1)*divisionWidth, (mouseYObjectSpace+1)*divisionHeight);
		glVertex2f(mouseXObjectSpace*divisionWidth, (mouseYObjectSpace+1)*divisionHeight);
		glEnd();
	}
	
	void display(){
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		if (CellMode) {
			gluOrtho2D(0, VIEW_DISTANCE, 0, VIEW_DISTANCE);
		}else {
			gluOrtho2D(-VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE);	
		}

	

		//glOrtho(-VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE);	
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glClear(GL_COLOR_BUFFER_BIT);
		if (CellMode) {
			DrawCellBorders();
			DrawCells();
			glColor4f(0.0, 0.9, 0.3, 1.0);
			
			if (DEBUG) {
				DrawSelectedCell();
			}
		}else {
			Draw2DCreatures();
		}

		glutSwapBuffers();
	}
	
	void reshape(int w, int h){
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(-VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE);
		//glOrtho(-VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE);
		glutPostRedisplay();
	}
};